import { AUTHORIZED_KEY, HttpStatusCode, LoginType } from '@/enums/request.enums';

import { useDeviceStore } from '@/store/deviceStore';

export abstract class BaseRequest {
  // 客户端标识
  private clientId: string;
  // 客户端密钥
  private clientSecret: string;
  // 登录类型
  private loginType: LoginType;

  // token 刷新状态
  private isRefreshToken: boolean = false;
  // 请求队列
  private requestList: Array<() => void> = [];
  // 登录状态
  private isLogin: boolean = false;

  // 租户
  private tenant_id: number = 1;

  // 构造函数
  constructor(clientId: string, clientSecret: string, loginType: LoginType) {
    this.clientId = clientId;
    this.clientSecret = clientSecret;
    this.loginType = loginType;
  }

  /**
   * 获取token
   */
  abstract getToken(): string;

  /**
   * 获取刷新token
   */
  abstract getRefreshTokenValue(): string;

  /**
   * 处理授权失败
   */
  abstract handleAuthorized(): void;

  /**
   * 设置token
   */
  abstract setTokenValue(accessToken: string, refreshToken: string): void;

  /**
   * 刷新令牌
   */
  private refreshToken(): Promise<{ accessToken: string, refreshToken: string }> {
    this.isRefreshToken = true;
    return new Promise((resolve, reject) => {
      const { buildRequestBaseURL } = useDeviceStore();
      let baseUrl = buildRequestBaseURL();
      if (baseUrl.endsWith('/')) {
        baseUrl = baseUrl.slice(0, -1);
      }
      uni
        .request({
          method: 'POST',
          header: {
            'tenant-id': this.tenant_id
          },
          url: `${baseUrl}/system/auth/refresh-token?refreshToken=${this.getRefreshTokenValue()}&clientId=${this.clientId}`
        })
        .then(res => {
          const data = res.data as ReqConstraint.Response<{ accessToken: string, refreshToken: string }>;

          if (
            res.statusCode !== HttpStatusCode.Ok
            || data.code !== HttpStatusCode.ServiceSuccess
          ) {
            return reject(res);
          }
          resolve(data.data as { accessToken: string, refreshToken: string });
        })
        .catch(err => {
          reject(err);
        });
    });
  }

  /**
   * 静默登录
   */
  private silentLogin(): Promise<void> {
    const { buildRequestBaseURL } = useDeviceStore();
    return new Promise<void>((resolve, reject) => {
      const _setToken = this.setTokenValue.bind(this);
      uni.request({
        url: `${buildRequestBaseURL()}system/oauth2/token`,
        method: 'POST',
        data: {
          client_id: this.clientId,
          client_secret: this.clientSecret,
          grant_type: 'client_credentials',
          scope: 'api,user.read',
          code: '001'
        },
        header: {
          'Content-Type': 'application/x-www-form-urlencoded',
          'tenant-id': this.tenant_id
        },
        success(response) {
          const data = response.data as ReqConstraint.Response<{
            access_token: string
            refresh_token: string
          }>;
          if (response.statusCode === HttpStatusCode.Ok && data.code === HttpStatusCode.ServiceSuccess) {
            _setToken(data.data.access_token, data.data.refresh_token);
            resolve();
          }
          else {
            reject(response);
          }
        },
        fail(err) {
          reject(err);
        }
      });
    });
  }

  /**
   * 发送请求
   */
  public request<T = any>(options: RequestOptions): Promise<T> {
    const { buildRequestBaseURL } = useDeviceStore();
    return new Promise<T>((resolve, reject) => {
      // 获取token
      const token = this.getToken();
      if (token) {
        options.header = options.header || {};
        options.header[AUTHORIZED_KEY] = `Bearer ${token}`;
      }

      // 静默登录处理
      if (!token && this.loginType === LoginType.Silent) {
        // 如果登录正在进行，则将当前请求放入队列
        if (this.isLogin || this.isRefreshToken) {
          return new Promise((innerResolve, innerReject) => {
            this.requestList.push(() => {
              this.request(options)
                .then(data => {
                  resolve(data);
                  innerResolve(data);
                })
                .catch(err => {
                  reject(err);
                  innerReject(err);
                });
            });
          });
        }

        // 如果登录没有进行，则执行登录
        this.isLogin = true;

        return this.silentLogin()
          .then(() => {
            this.isLogin = false;
            // 执行队列中所有等待的请求
            this.requestList.forEach(cb => cb());
            this.requestList = [];
            // 登录成功后，重新发起当前请求
            return resolve(this.request(options));
          })
          .catch(err => {
            this.isLogin = false;
            this.requestList.forEach(cb => cb());
            this.requestList = [];
            console.log('>>>>登录失败', err);
            return reject(err);
          });
      }

      const url = options.url.startsWith('/')
        ? options.url.slice(1)
        : options.url;

      const requestTask = uni.request({
        ...options,
        url: buildRequestBaseURL() + url,
        header: {
          ...(options.header || {}),
          'tenant-id': this.tenant_id
        },
        success: async result => {
          const data = result.data as ReqConstraint.Response<T>;

          // 判断是否过滤
          if (options.skipInterceptor) {
            resolve(data as T);
            return;
          }

          // 请求成功
          if (data.code === HttpStatusCode.ServiceSuccess) {
            resolve(data.data as T);
          }
          else {
            // 登录过期
            if (data.code === HttpStatusCode.Unauthorized) {
              // 未授权，尝试刷新token
              if (!this.isRefreshToken) {
                // 判断是否有refreshToken
                if (!this.getRefreshTokenValue()) {
                  this.handleAuthorized();
                  return reject(data);
                }

                try {
                  // 尝试刷新token
                  const refreshTokenRes = await this.refreshToken();

                  // 设置新token
                  this.setTokenValue(
                    refreshTokenRes.accessToken,
                    refreshTokenRes.refreshToken
                  );

                  this.isRefreshToken = false;

                  // 执行队列中所有等待的请求
                  this.requestList.forEach(cb => cb());
                  this.requestList = [];

                  // 重新发起当前请求并返回结果
                  return resolve(this.request(options));
                }
                catch (err) {
                  this.isRefreshToken = false;

                  // 刷新token失败，执行队列中所有等待的请求（都会失败）
                  this.requestList.forEach(cb => cb());
                  this.requestList = [];

                  this.handleAuthorized();
                  return reject(err);
                }
              }
              else {
                // 其他请求也需要刷新token，加入等待队列
                return new Promise(innerResolve => {
                  this.requestList.push(() => {
                    this.request(options)
                      .then(innerData => {
                        resolve(innerData);
                        innerResolve();
                      })
                      .catch(innerErr => {
                        reject(innerErr);
                        innerResolve();
                      });
                  });
                });
              }
            }
            else {
              if (!options.hideRejectToast) {
                uni.showToast({
                  title: data.msg || '数据请求失败',
                  icon: 'none'
                });
              }
              reject(data);
            }
          }
        },
        fail: err => {
          if (!options.hideRejectToast) {
            uni.showToast({
              title: '数据请求失败',
              icon: 'none'
            });
          }
          reject(err);
        }
      });

      // 判断是否配置中断请求
      if (options.signal) {
        options.signal.onabort = function () {
          requestTask.abort();
          reject(new Error('abort'));
        };
      }
    });
  }
}
